package com.ssy.lingxi.product.serviceImpl.platform;

import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.member.merchant.api.model.vo.inner.request.MemberFeignVO;
import com.ssy.lingxi.product.common.enums.MaterialProcessEnum;
import com.ssy.lingxi.product.common.enums.MaterialProcessStatusEnum;
import com.ssy.lingxi.product.common.enums.MaterialProcessTypeEnum;
import com.ssy.lingxi.product.entity.material.BaseMaterialProcessDO;
import com.ssy.lingxi.product.entity.material.MaterialProcessDO;
import com.ssy.lingxi.product.entity.platform.PlatformMaterialProcessDO;
import com.ssy.lingxi.product.entity.platform.PlatformMaterialProcessMemberDO;
import com.ssy.lingxi.product.model.dto.platform.PlatformMaterialProcessMemberDTO;
import com.ssy.lingxi.product.model.request.platform.*;
import com.ssy.lingxi.product.model.resp.platform.PlatformBaseMaterialProcessVO;
import com.ssy.lingxi.product.model.resp.platform.PlatformMaterialProcessDetailVO;
import com.ssy.lingxi.product.model.resp.platform.PlatformMaterialProcessPageQueryVO;
import com.ssy.lingxi.product.repository.material.BaseMaterialProcessRepository;
import com.ssy.lingxi.product.repository.material.MaterialProcessRepository;
import com.ssy.lingxi.product.repository.platform.PlatformMaterialProcessMemberRepository;
import com.ssy.lingxi.product.repository.platform.PlatformMaterialProcessRepository;
import com.ssy.lingxi.product.service.feign.IMemberFeignService;
import com.ssy.lingxi.product.service.platform.IPlatformMaterialProcessService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 平台后台 - 物料流程配置 服务接口实现
 * @author 彭冬冬
 * @version 2.0.0
 * @date 2022/3/25 14:20
 */
@Service
public class PlatformMaterialProcessServiceImpl implements IPlatformMaterialProcessService {

    @Resource
    private PlatformMaterialProcessRepository repository;

    @Resource
    private BaseMaterialProcessRepository baseMaterialProcessRepository;

    @Resource
    private PlatformMaterialProcessMemberRepository memberRepository;

    @Resource
    private MaterialProcessRepository materialProcessRepository;

    @Resource
    private IMemberFeignService memberFeignService;

    @Override
    public Wrapper<PageData<PlatformMaterialProcessPageQueryVO>> pageMaterialProcess(UserLoginCacheDTO sysUser, PlatformMaterialProcessPageVO pageVO) {
        // step 1:构建查询条件
        // 分页查询
        Pageable pageable = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("createTime").descending());
        // 查询条件
        Specification<PlatformMaterialProcessDO> spec = (root, query, builder) -> {
            List<Predicate> list = new ArrayList<>();
            if (StringUtils.hasText(pageVO.getName())) {
                list.add(builder.like(root.get("name").as(String.class), "%".concat(pageVO.getName()).concat("%")));
            }
            return query.where(list.toArray(new Predicate[0])).getRestriction();
        };
        // step 2:查询数据
        Page<PlatformMaterialProcessDO> pageList = repository.findAll(spec, pageable);
        // step 3:组装数据
        return Wrapper.success(new PageData<>(pageList.getTotalElements(), pageList.getContent().stream().map(this::builderPlatformMaterialProcessPageQueryVO).collect(Collectors.toList())));
    }

    /**
     * 将数据库的平台物料流程规则配置组装成返回给前端的结构
     * @param entity 数据库的平台物料流程规则配置
     * @return 给前端的结构
     */
    private PlatformMaterialProcessPageQueryVO builderPlatformMaterialProcessPageQueryVO(PlatformMaterialProcessDO entity) {
        PlatformMaterialProcessPageQueryVO target = new PlatformMaterialProcessPageQueryVO();
        target.setProcessId(entity.getId());
        target.setName(entity.getName());
        target.setStatus(entity.getStatus());
        target.setStatusName(MaterialProcessStatusEnum.getMessage(entity.getStatus()));
        target.setCreateTime(entity.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        return target;
    }

    @Override
    public Wrapper<List<PlatformBaseMaterialProcessVO>> listBaseMaterialProcess(UserLoginCacheDTO sysUser) {
        List<PlatformBaseMaterialProcessVO> list = baseMaterialProcessRepository.findAll(Sort.by("id").ascending()).stream().map(this::builderPlatformBaseMaterialProcessVO).collect(Collectors.toList());
        return Wrapper.success(list);
    }

    private PlatformBaseMaterialProcessVO builderPlatformBaseMaterialProcessVO(BaseMaterialProcessDO baseMaterialProcessDO) {
        PlatformBaseMaterialProcessVO target = new PlatformBaseMaterialProcessVO();
        target.setBaseProcessId(baseMaterialProcessDO.getId());
        target.setProcessName(MaterialProcessEnum.getNameByCode(baseMaterialProcessDO.getCode()));
        target.setProcessType(baseMaterialProcessDO.getProcessType());
        target.setProcessTypeName(MaterialProcessTypeEnum.getMessage(baseMaterialProcessDO.getProcessType()));
        target.setDescription(MaterialProcessEnum.getRemarkByCode(baseMaterialProcessDO.getCode()));
        return target;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> save(UserLoginCacheDTO sysUser, PlatformMaterialProcessVO saveVO) {
        // step 1:校验
        Wrapper<BaseMaterialProcessDO> checkWrapper = checkSave(saveVO);
        if (checkWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(checkWrapper);
        }
        BaseMaterialProcessDO baseMaterialProcess = checkWrapper.getData();
        // step 2:组装数据
        PlatformMaterialProcessDO entity = builderEntity(saveVO, baseMaterialProcess);
        // step 3:保存
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    /**
     * 根据新增数据构建保存数据库数据
     * @param saveVO 前端传输数据
     * @param baseMaterialProcess 基础物料流程
     * @return 保存数据库的格式
     */
    private PlatformMaterialProcessDO builderEntity(PlatformMaterialProcessVO saveVO, BaseMaterialProcessDO baseMaterialProcess) {
        PlatformMaterialProcessDO entity = new PlatformMaterialProcessDO();
        entity.setCreateTime(LocalDateTime.now());
        entity.setName(saveVO.getName());
        entity.setProcessKey(baseMaterialProcess.getProcessKey());
        entity.setProcessType(baseMaterialProcess.getProcessType());
        entity.setStatus(MaterialProcessStatusEnum.ENABLE.getCode());
        entity.setAllMembers(saveVO.getAllMembers());
        entity.setProcess(baseMaterialProcess);
        List<PlatformMaterialProcessMemberDO> members = builderPlatformMaterialProcessMemberDO(entity, saveVO);
        entity.setMembers(members);
        return entity;
    }

    /**
     * 构建关联平台物料流程会员数据
     * @param entity 平台物料流程
     * @param saveVO 保存物料流程数据
     * @return 关联平台物料流程会员数据
     */
    private List<PlatformMaterialProcessMemberDO> builderPlatformMaterialProcessMemberDO(PlatformMaterialProcessDO entity, PlatformMaterialProcessVO saveVO) {
        List<PlatformMaterialProcessMemberDO> targetList = new ArrayList<>();
        if (CollectionUtils.isEmpty(saveVO.getMembers())) {
            return targetList;
        }
        List<PlatformMaterialProcessMemberDO> members = saveVO.getMembers().stream().map(map -> {
            PlatformMaterialProcessMemberDO target = new PlatformMaterialProcessMemberDO();
            target.setProcess(entity);
            target.setMemberId(map.getMemberId());
            target.setRoleId(map.getRoleId());
            return target;
        }).collect(Collectors.toList());
        targetList.addAll(members);
        return targetList;
    }

    /**
     * 保存校验，同时获取基础物料流程
     * @param saveVO 保存数据
     * @return 基础物料流程
     */
    private Wrapper<BaseMaterialProcessDO> checkSave(PlatformMaterialProcessVO saveVO) {
        // step 1:判断基础流程
        BaseMaterialProcessDO baseMaterialProcess = baseMaterialProcessRepository.findById(saveVO.getBaseProcessId()).orElse(null);
        if (Objects.isNull(baseMaterialProcess)) {
            return Wrapper.fail(ResponseCode.PRODUCT_BASE_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        // step 2:判断是否已经存在
        Specification<PlatformMaterialProcessDO> spec = (root, query, builder) -> builder.equal(root.get("process").as(BaseMaterialProcessDO.class), baseMaterialProcess);
        List<PlatformMaterialProcessDO> all = repository.findAll(spec);
        if (!CollectionUtils.isEmpty(all)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_EXISTS);
        }
        // step 3:校验、保存关联的会员
        if (!saveVO.getAllMembers() && CollectionUtils.isEmpty(saveVO.getMembers())) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_MEMBER_IS_EMPTY);
        }
        return Wrapper.success(baseMaterialProcess);
    }

    @Override
    public Wrapper<PlatformMaterialProcessDetailVO> getInfo(UserLoginCacheDTO sysUser, Long processId) {
        // step 1: 校验数据
        PlatformMaterialProcessDO entity = repository.findById(processId).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }

        // step 2: 组装数据
        PlatformMaterialProcessDetailVO target = new PlatformMaterialProcessDetailVO();
        target.setProcessId(entity.getId());
        target.setName(entity.getName());
        target.setStatus(entity.getStatus());
        target.setStatusName(MaterialProcessStatusEnum.getMessage(entity.getStatus()));
        target.setBaseProcessId(Optional.ofNullable(entity.getProcess()).map(BaseMaterialProcessDO::getId).orElse(0L));
        target.setAllMembers(entity.getAllMembers());
        baseMaterialProcessRepository.findById(entity.getProcess().getId()).ifPresent(p -> {
            PlatformBaseMaterialProcessVO baseMaterialProcessVO = builderPlatformBaseMaterialProcessVO(p);
            target.setBaseProcess(baseMaterialProcessVO);
        });
        return Wrapper.success(target);
    }

    @Override
    public Wrapper<List<PlatformMaterialProcessMemberDTO>> listMaterialProcessMembers(UserLoginCacheDTO sysUser, PlatformMaterialProcessMemberPageVO pageVO) {
        // step 1:校验数据
        PlatformMaterialProcessDO entity = repository.findById(pageVO.getProcessId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        if (entity.getAllMembers()) {
            return Wrapper.success( new ArrayList<>());
        }
        Specification<PlatformMaterialProcessMemberDO> spec = (root, query, builder) -> builder.equal(root.get("process").as(PlatformMaterialProcessDO.class), entity);
        List<PlatformMaterialProcessMemberDO> members = memberRepository.findAll(spec, Sort.by("id").descending());
        if (CollectionUtils.isEmpty(members)) {
            return Wrapper.success( new ArrayList<>());
        }
        // step 2:从会员服务查询
        List<MemberFeignVO> feignList = members.stream().map(map -> {
            MemberFeignVO feignVO = new MemberFeignVO();
            feignVO.setMemberId(map.getMemberId());
            feignVO.setRoleId(map.getRoleId());
            return feignVO;
        }).collect(Collectors.toList());
        Wrapper<List<PlatformMaterialProcessMemberDTO>> feignResult = memberFeignService.findPlatformMembers(pageVO.getName(), feignList);
        if(feignResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(feignResult.getCode(), feignResult.getMessage());
        }
        if(CollectionUtils.isEmpty(feignResult.getData())) {
            return Wrapper.success(new ArrayList<>());
        }

        return Wrapper.success(feignResult.getData());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> update(UserLoginCacheDTO sysUser, PlatformMaterialProcessUpdateVO updateVO) {
        // step 1: 修改校验
        Wrapper<PlatformMaterialProcessDO> checkWrapper = checkUpdate(updateVO);
        if (checkWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(checkWrapper);
        }

        // step 2:删除历史关联会员数据
        List<PlatformMaterialProcessMemberDO> oldMembers = repository.findById(updateVO.getProcessId()).map(PlatformMaterialProcessDO::getMembers).orElse(null);
        if (!CollectionUtils.isEmpty(oldMembers)) {
            memberRepository.deleteInBatch(oldMembers);
        }
        // step 3:组装数据
        PlatformMaterialProcessDO entity = checkWrapper.getData();
        entity.setCreateTime(LocalDateTime.now());
        entity.setName(updateVO.getName());
        entity.setProcessKey(entity.getProcess().getProcessKey());
        entity.setProcessType(entity.getProcess().getProcessType());
        entity.setStatus(entity.getStatus());
        entity.setAllMembers(updateVO.getAllMembers());
        entity.setMembers(builderPlatformMaterialProcessMemberDO(entity, updateVO));
        // step 4:保存数据
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    /**
     * 校验修改平台物料流程
     * @param updateVO 更新平台物料流程
     * @return 修改结果
     */
    private Wrapper<PlatformMaterialProcessDO> checkUpdate(PlatformMaterialProcessUpdateVO updateVO) {
        // step 1:判断基础流程
        BaseMaterialProcessDO baseMaterialProcess = baseMaterialProcessRepository.findById(updateVO.getBaseProcessId()).orElse(null);
        if (Objects.isNull(baseMaterialProcess)) {
            return Wrapper.fail(ResponseCode.PRODUCT_BASE_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        Specification<PlatformMaterialProcessDO> spec = (root, query, builder) -> query.where(builder.equal(root.get("process").as(BaseMaterialProcessDO.class), baseMaterialProcess),builder.notEqual(root.get("id").as(Long.class), updateVO.getProcessId())).getRestriction();
        List<PlatformMaterialProcessDO> all = repository.findAll(spec);
        if (!CollectionUtils.isEmpty(all)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_EXISTS);
        }
        // step 3:校验是否存在物料流程
        PlatformMaterialProcessDO entity = repository.findById(updateVO.getProcessId()).orElse(null);
        if(Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        if (Objects.equals(entity.getStatus(), MaterialProcessStatusEnum.ENABLE.getCode())) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_NOT_UPDATE_PERMISSION);
        }
        // step 4:校验、保存关联的会员
        if (!updateVO.getAllMembers() && CollectionUtils.isEmpty(updateVO.getMembers())) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_MEMBER_IS_EMPTY);
        }
        entity.setProcess(baseMaterialProcess);
        return Wrapper.success(entity);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> updateStatus(UserLoginCacheDTO sysUser, PlatformMaterialProcessUpdateStatusVO updateStatusVO) {
        // step 1:校验数据是否存在
        PlatformMaterialProcessDO entity = repository.findById(updateStatusVO.getProcessId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        // step 2:交易冻结时
        if (Objects.equals(updateStatusVO.getStatus(), MaterialProcessStatusEnum.DISABLE.getCode())) {
            // 查看是否默认所有会员
            if (entity.getAllMembers()) {
                Specification<MaterialProcessDO> spec = (root, query, builder) -> builder.equal(root.get("processKey").as(String.class), entity.getProcessKey());
                List<MaterialProcessDO> materialProcessDOList = materialProcessRepository.findAll(spec);
                if (!CollectionUtils.isEmpty(materialProcessDOList)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_NOT_UPDATE_PERMISSION);
                }
            } else {
                Specification<MaterialProcessDO> spec = (root, query, builder) -> {
                    Predicate processKey = builder.equal(root.get("processKey").as(String.class), entity.getProcessKey());
                    Predicate[] predicates = entity.getMembers().stream().map(map -> {
                        Predicate memberId = builder.equal(root.get("memberId").as(Long.class), map.getMemberId());
                        Predicate roleId = builder.equal(root.get("roleId").as(Long.class), map.getRoleId());
                        return builder.and(memberId, roleId);
                    }).toArray(Predicate[]::new);
                    Predicate or = builder.or(predicates);
                    return query.where(processKey, or).getRestriction();
                };
                List<MaterialProcessDO> materialProcessDOList = materialProcessRepository.findAll(spec);
                if (!CollectionUtils.isEmpty(materialProcessDOList)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_NOT_UPDATE_PERMISSION);
                }
            }
        }
        // step 3:修改状态
        entity.setStatus(updateStatusVO.getStatus());
        entity.setCreateTime(LocalDateTime.now());
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> delete(UserLoginCacheDTO sysUser, Long processId) {
        // step 1:校验删除数据
        PlatformMaterialProcessDO entity = repository.findById(processId).orElse(null);
        if(Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        if (Objects.equals(entity.getStatus(), MaterialProcessStatusEnum.ENABLE.getCode())) {
            return Wrapper.fail(ResponseCode.PRODUCT_PLATFORM_MATERIAL_NOT_DELETE_PERMISSION);
        }
        repository.deleteById(processId);
        return Wrapper.success();
    }
}
